<?php


namespace common\service;


use common\model\EntityModel;
use common\model\ProjectModel;
use common\model\StepWorkflowModel;
use common\model\TaskModel;
use think\module\Module;

class ProjectOkrService
{

    protected $totalStatus = [
        "out_of_date",
        "warning",
        "done",
        "un_done",
        "progressing",
        "not_start",
    ];

    /**
     * 项目okr统计
     * @param int $projectId
     * @param array $statusNeeded
     * @return array[]
     */
    public function getProjectOkr(int $projectId, array $statusNeeded)
    {
        $nodes = [];
        $edges = [];

        $project = model(ProjectModel::class)->find($projectId);
        $project['real_id'] = $project['id'];
        $project['id'] = 'project' . $projectId;
        $project['type'] = "project";

        // 所有的集
        $episodeModule = Module::$moduleDictData['module_index_by_code']['episode'];
        $episodes = model(EntityModel::class)->where(['project_id' => $projectId, 'module_id' => $episodeModule['id']])->order("code DESC")->select();

        $episodesCode = array_column($episodes, 'code');
        // 逾期任务查询


        $filter = [
            'task.project_id' => $projectId,
            'entity.initial_episode_code' => ['IN', implode(',', $episodesCode)],
        ];

        $groupKey = $fields = "entity.initial_episode_code";

        list($outOfDateTask, $warningTask, $unDoneTask, $progressTask) = $this->getStatusTask($filter, $groupKey, $fields);


        $outOfDateTask = array_column($outOfDateTask, 'initial_episode_code');
        $warningTask = array_column($warningTask, 'initial_episode_code');
        $unDoneTask = array_column($unDoneTask, 'initial_episode_code');
        $progressTask = array_column($progressTask, 'initial_episode_code');

        $episodes = $this->handleStatusText($episodes, ['out_of_date' => $outOfDateTask, 'warning' => $warningTask, 'un_done' => $unDoneTask, 'progressing' => $progressTask], "code");

        //集最大完成时间


        $taskDoneTime = model(TaskModel::class)
            ->join("entity ON entity.id = task.entity_id")
            ->where([
                'entity.initial_episode_code' => ['in', implode(',', $episodesCode)],
                'task.end_time' => ['gt', 0]
            ])->group("entity.initial_episode_code")
            ->field("entity.initial_episode_code as episode_code,max(task.end_time) as done_time")
            ->select();

        $taskDoneTime = array_column($taskDoneTime, null, 'episode_code');


        $projectStatus = "";
        $statusSort = array_flip($this->totalStatus);

        foreach ($episodes as $index => &$episode) {

            if (empty($projectStatus) || $statusSort[$episode['okr_status']] < $statusSort[$projectStatus]) {
                $projectStatus = $episode['okr_status'];
            }

            // 隐藏不需要的状态数据
            if (!in_array($episode['okr_status'], $statusNeeded)) {
                continue;
            }

            $episode['type'] = "episode";
            $episode['real_id'] = $episode['id'];
            $episode['id'] = "episode" . $episode['id'];
            $episode['done_time'] = 0;
            if (in_array($episode['code'], $taskDoneTime)) {
                $episode['done_time'] = $taskDoneTime['done_time'];
            }
            $nodes[] = $episode;

            $edges[] = [
                "from" => $project['id'],
                'to' => $episode['id']
            ];
        }
        $project['okr_status'] = $projectStatus;
        $nodes[] = $project;

        return [
            'nodes' => $nodes,
            'edges' => $edges,
        ];
    }

    /**
     * okr统计
     * @param $param
     * @return array|array[]
     */
    public function getOkrStatistics($param)
    {
        $projectId = $param['project_id'];
        $type = $param['type'] ?? "project";
        $latestStatus = $param['okr_status'] ?? "not_start";
        $statusNeeded = $param['status_needed'] ?? $this->totalStatus;

        $statusNeeded = array_intersect($this->totalStatus, $statusNeeded);

        $res = [];
        if ($type == "project") {
            $res = $this->getProjectOkr($projectId, $statusNeeded);
        }

        if ($type == "entity") {
            $res = $this->getEntityOkr($param['id'], $latestStatus, $statusNeeded);
        }

        if ($type == "episode") {
            $res = $this->getEpisodeOkr($param['id'], $latestStatus, $statusNeeded);
        }
        return $res;
    }

    /**
     * 集okr查询
     * @param int $episodeId
     * @param string $latestStatus
     * @return array
     */
    private function getEpisodeOkr(int $episodeId, string $latestStatus, array $statusNeeded)
    {


        $episode = model(EntityModel::class)->find($episodeId);


        $shotModule = Module::$moduleDictData['module_index_by_code']['shot'];

        $shots = model(EntityModel::class)
            ->where([
                'initial_episode_code' => $episode['code'],
                'module_id' => $shotModule['id'],
                'project_id' => $episode['project_id']
            ])
            ->field("id,name,code")
            ->select();

        $shotIds = array_column($shots, 'id');


        $filter = [
            'entity.id' => ['in', implode(',', $shotIds)],
        ];
        $groupKey = $fields = "entity.id";

        list($outOfDateTask, $warningTask, $unDoneTask, $progressTask) = $this->getStatusTask($filter, $groupKey, $fields);

        $outOfDateTask = array_column($outOfDateTask, 'id');
        $warningTask = array_column($warningTask, 'id');
        $unDoneTask = array_column($unDoneTask, 'id');
        $progressTask = array_column($progressTask, 'id');
        $shots = $this->handleStatusText($shots, [
            'out_of_date' => $outOfDateTask,
            'warning' => $warningTask,
            'un_done' => $unDoneTask,
            'progressing' => $progressTask
        ], "id");


        // 除开镜头之外存在的分类任务
        $subcategories = model(EntityModel::class)
            ->join("task ON entity.id = task.entity_id")
            ->where([
                'task.project_id' => $episode['project_id'],
                'entity.initial_episode_code' => $episode['code'],
                'task.step_type' => 'step_category',
                'entity.module_id' => ['neq', $shotModule['id']]
            ])->join("step_category ON step_category.id = task.step_category_id")
            ->field("step_category.*")
            ->group("task.step_category_id")
            ->select();


        $stepCategoryIdData = array_column($subcategories, 'id');


        $filter = [
            'task.step_category_id' => ['in', implode(',', $stepCategoryIdData)],
            'task.project_id' => $episode['project_id'],
            'entity.initial_episode_code' => $episode['code'],
        ];
        $groupKey = $fields = "task.step_category_id";

        list($outOfDateTask, $warningTask, $unDoneTask, $progressTask) = $this->getStatusTask($filter, $groupKey, $fields);

        $outOfDateTask = array_column($outOfDateTask, 'step_category_id');
        $warningTask = array_column($warningTask, 'step_category_id');
        $unDoneTask = array_column($unDoneTask, 'step_category_id');
        $progressTask = array_column($progressTask, 'step_category_id');
        $subcategories = $this->handleStatusText($subcategories, [
            'out_of_date' => $outOfDateTask,
            'warning' => $warningTask,
            'un_done' => $unDoneTask,
            'progressing' => $progressTask
        ], 'id');


        foreach ($subcategories as &$subcategory) {
            $subcategory['type'] = 'step_category';
        }
        $nodes = [];
        $edges = [];
        $tmpNode = $episode;
        $tmpNode['real_id'] = $episode['id'];
        $tmpNode['id'] = "episode" . $episode['id'];
        $tmpNode['okr_status'] = $latestStatus;
        $tmpNode['type'] = "episode";
        $nodes[] = $tmpNode;


        foreach ($shots as $shot) {
            // 隐藏不需要的状态数据
            if (!in_array($shot['okr_status'], $statusNeeded)) {
                continue;
            }
            $shot['type'] = 'entity';
            $shot['real_id'] = $shot['id'];
            $shot['id'] = 'entity_' . $shot['id'];
            $nodes[] = $shot;
            $edges[] = [
                "from" => $tmpNode['id'],
                'to' => $shot['id']
            ];
        }

        foreach ($subcategories as $stepCategory) {
            // 隐藏不需要的状态数据
            if (!in_array($stepCategory['okr_status'], $statusNeeded)) {
                continue;
            }
            $subcategory['type'] = 'step_category';
            $stepCategory['real_id'] = $stepCategory['id'];
            $stepCategory['id'] = 'step_category_' . $stepCategory['id'];
            $nodes[] = $stepCategory;
            $edges[] = [
                "from" => $tmpNode['id'],
                'to' => $stepCategory['id']
            ];
        }

        return [
            'nodes' => $nodes,
            'edges' => $edges,
            'root_id' => $tmpNode['id'],
        ];
    }

    /**
     * 实体okr统计
     * @param int $entityId
     * @param string $latestStatus
     * @param array $statusNeeded
     * @return array
     */
    private function getEntityOkr(int $entityId, string $latestStatus, array $statusNeeded)
    {
        $nodes = [];
        $edges = [];
        $entity = model(EntityModel::class)->find($entityId);
        $tmpNode = $entity;
        $tmpNode['real_id'] = $entityId;
        $tmpNode['type'] = "entity";
        $tmpNode['id'] = "entity_" . $entityId;
        $tmpNode['okr_status'] = $latestStatus;
        $nodes[] = $tmpNode;


        $sequenceModule = Module::$moduleDictData['module_index_by_code']['sequence'];
        $shotModule = Module::$moduleDictData['module_index_by_code']['shot'];
        $assetModule = Module::$moduleDictData['module_index_by_code']['asset'];
        $levelModule = Module::$moduleDictData['module_index_by_code']['level'];


        $workflow = model(StepWorkflowModel::class)->field("json_extract(config,'$.nodes') as nodes,json_extract(config,'$.edges') as edges")->find($entity['step_workflow_id']);
        if (!empty($workflow)) {
            $stepIds = [];
            $workflow['nodes'] = json_decode($workflow['nodes'], true);
            $workflow['edges'] = json_decode($workflow['edges'], true);
            foreach ($workflow['nodes'] as &$stepWorkflowNodes) {
                foreach ($stepWorkflowNodes as &$node) {
                    $stepIds[] = $node['id'];
//                            $node['status'] = "done";
                }
            }
            $filter = [
                'entity.id' => $entityId,
            ];
            $groupKey = $fields = "task.id";

            $tasks = model(TaskModel::class)->where(['entity_id' => $entityId])->select();

            list($outOfDateTask, $warningTask, $unDoneTask, $progressTask) = $this->getStatusTask($filter, $groupKey, $fields);

            $outOfDateTask = array_column($outOfDateTask, 'id');
            $warningTask = array_column($warningTask, 'id');
            $unDoneTask = array_column($unDoneTask, 'id');
            $progressTask = array_column($progressTask, 'id');
            $tasks = $this->handleStatusText($tasks, [
                'out_of_date' => $outOfDateTask,
                'warning' => $warningTask,
                'un_done' => $unDoneTask,
                'progressing' => $progressTask
            ], 'id');
            foreach ($tasks as $task) {
                $task['type'] = 'task';
                $task['real_id'] = $task['id'];
                $task['id'] = 'task' . $task['id'];
                $nodes[] = $task;
                $edges[] = [
                    "from" => $tmpNode['id'],
                    'to' => $task['id']
                ];
            }
        }

        $shots = [];
        $assets = [];
        $levels = [];
        switch ($entity['module_id']) {
            case $sequenceModule['id']:
                //序列关联的镜头 资产 关卡
                $shots = model(EntityModel::class)->where(['entity_id' => $entity['id'], 'entity_module_id' => $sequenceModule['id']])->select();
                $json = json_decode($entity['json'], true);
                if (!empty($json['sequence_asset'])) {
                    $assets = model(EntityModel::class)->where(['id' => ['IN', $json['sequence_asset']]])->select();
                }
                if (!empty($json['sequence_level'])) {
                    $levels = model(EntityModel::class)->where(['id' => ['IN', $json['sequence_level']]])->select();
                }
                break;
            case $shotModule['id']:
                // 镜头属于被关联

                /**
                 *
                 * 被序列关联
                 *
                 * 序列有关联的资产
                 *
                 */

                // 找到父级序列
                $sequence = model(EntityModel::class)->where(['id' => $entity['entity_id'], 'module_id' => $sequenceModule['id']])->find();

                if (!empty($sequence)) {
                    $json = json_decode($sequence['json'], true);
                    if (!empty($json['sequence_asset'])) {
                        $assets = model(EntityModel::class)->where(['id' => ['IN', $json['sequence_asset']]])->select();
                    }
                    if (!empty($json['sequence_level'])) {
                        $levels = model(EntityModel::class)->where(['id' => ['IN', $json['sequence_level']]])->select();
                    }
                }

                break;
            case $assetModule['id']:
                // 资产被关联

                /**
                 * 被序列关联
                 *
                 * 序列查询关联的镜头
                 */
                $sequences = model(EntityModel::class)->where("FIND_IN_SET({$entityId},JSON_UNQUOTE(JSON_EXTRACT(json,'$.sequence_asset')))")->select();

                if (!empty($sequences)) {
                    $sequenceIdArray = array_column($sequences, 'id');
                    $shots = model(EntityModel::class)->where(['entity_id' => ['IN', implode(',', $sequenceIdArray)], 'entity_module_id' => $sequenceModule['id']])->select();
                }
                break;
            case $levelModule['id']:
                // 关卡被关联

                /**
                 *
                 * 被序列关联
                 *
                 * 查询序列之后 查询出关联的镜头
                 *
                 */
//                $sequences = model(EntityModel::class)->where("FIND_IN_SET({$entityId},JSON_UNQUOTE(JSON_EXTRACT(json,'$.sequence_asset')))")->select();


            default:
        }

        $relatedEntity = array_merge($shots, $assets, $levels);

        $entityIds = array_column($relatedEntity, 'id', 'id');

        $filter = [
            'entity.id' => ['in', implode(',', $entityIds)],
        ];
        $groupKey = $fields = "entity.id";

        list($outOfDateTask, $warningTask, $unDoneTask, $progressTask) = $this->getStatusTask($filter, $groupKey, $fields);

        $outOfDateTask = array_column($outOfDateTask, 'id');
        $warningTask = array_column($warningTask, 'id');
        $unDoneTask = array_column($unDoneTask, 'id');
        $progressTask = array_column($progressTask, 'id');
        $relatedEntity = $this->handleStatusText($relatedEntity, [
            'out_of_date' => $outOfDateTask,
            'warning' => $warningTask,
            'un_done' => $unDoneTask,
            'progressing' => $progressTask
        ], 'id');
        foreach ($relatedEntity as $entityItem) {
            // 隐藏不需要的状态数据
            if (!in_array($entityItem['okr_status'], $statusNeeded)) {
                continue;
            }
            $entityItem['real_id'] = $entityItem['id'];
            $entityItem['type'] = 'entity';
            $entityItem['id'] = 'entity_' . $entityItem['id'];
            $nodes[] = $entityItem;
            $edges[] = [
                "from" => $tmpNode['id'],
                'to' => $entityItem['id']
            ];
        }

        return [
            'nodes' => $nodes,
            'edges' => $edges,
            'root_id' => $tmpNode['id'],
        ];


    }

    /**
     * 查询出各个状态的任务
     * @param $filter
     * @param $groupKey
     * @param $fields
     * @return array
     */
    public static function getStatusTask($filter, $groupKey, $fields)
    {
        $statusService = StatusService::getInstance();
        $taskDoneStatus = $statusService->getTaskDoneStatus();

        /**
         * 逾期任务
         */
        $outDate = model(TaskModel::class)
            ->join("entity ON entity.id = task.entity_id")
            ->where([
                ['task.plan_end_time' => ['LT', time()],],
                [
                    'task.plan_end_time' => ['gt', 0],
                    'task.plan_start_time' => ['gt', 0]
                ],
            ])
            ->where($filter)
            ->where("JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) != {$taskDoneStatus['id']}")
            ->field($fields)
            ->group($groupKey)
            ->select();

        /**
         *
         */
        $warning = model(TaskModel::class)
            ->join("entity ON entity.id = task.entity_id")
            ->where($filter)
            ->where([
                [
                    // 距离任务计划开始时间还有7+7天，任务没有产能
                    [
                        "_string" => "task.actual_man_hour is null or task.actual_man_hour = ''",
                        "task.plan_start_time" => ['lt', strtotime("+14 days")],
                        "task.step_type" => "step"
                    ],
                    // 距离任务计划开始时间还有7-1天，任务未分配给组长
                    [
                        "task.plan_start_time" => ['lt', strtotime("+6 days")],
                        "task.step_type" => "step_category",
                        "task.executor" => 0
                    ],
                    // 距离任务计划开始时间还有1天，任务未分配给组员
                    [
                        "task.plan_start_time" => ['lt', strtotime("+1 days")],
                        "task.step_type" => "step",
                        "task.executor" => 0
                    ],
                    // 任务分配给组长/组员后1天，组长/组员未接收任务
                    [
                        "task.assignee_at" => ['gt', strtotime("-1 days")],
                        "task.executor_confirmed" => "no",
                    ],
                    // 任务预计开始时间+1天，组员未开始任务，且任务还没逾期的话
                    [
                        "task.plan_start_time" => ['gt', strtotime("-1 days")],
                        "task.plan_end_time" => ['gt', time()],
                        "task.step_type" => "step",
                        "task.start_time" => 0,
                    ],
                    "_logic" => "or"
                ],
                [
                    'task.plan_end_time' => ['gt', 0],
                    'task.plan_start_time' => ['gt', 0]
                ],
                "_logic" => "and"
            ])
            ->field($fields)
            ->group($groupKey)
            ->select();


        // 是否还存在未完成任务
        $unDone = model(TaskModel::class)
            ->join("entity ON entity.id = task.entity_id")
            ->where([
                'task.end_time' => 0,
                "_logic" => "and"
            ])
            ->where($filter)
            ->field($fields)
            ->group($groupKey)
            ->select();


        // 进行中任务
        $progress = model(TaskModel::class)
            ->join("entity ON entity.id = task.entity_id")
            ->where([
                'task.plan_end_time' => ['gt', 0],
                'task.plan_start_time' => ['gt', 0],
                'task.start_time' => ['gt', 0],
                'task.end_time' => 0,
                "_logic" => "and"
            ])
            ->where($filter)
            ->field($fields)
            ->group($groupKey)
            ->select();

        return [$outDate, $warning, $unDone, $progress];
    }

    /**
     * 处理状态
     * @param $data
     * @param $statusTasks
     * @param $dataKey
     * @return mixed
     */
    private function handleStatusText($data, $statusTasks, $dataKey)
    {
        foreach ($data as &$item) {
            if (in_array($item[$dataKey], $statusTasks['out_of_date'])) {
                $item['okr_status'] = "out_of_date";
                continue;
            }
            if (in_array($item[$dataKey], $statusTasks['warning'])) {
                $item['okr_status'] = "warning";
                continue;
            }

            if (!in_array($item[$dataKey], $statusTasks['un_done'])) {
                $item['okr_status'] = "done";
                continue;
            }

            if (in_array($item[$dataKey], $statusTasks['progressing'])) {
                $item['okr_status'] = "progressing";
                continue;
            }
            $item['okr_status'] = "not_start";
        }
        return $data;
    }
}